home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (c) 1992 The Geometry Center; University of Minnesota
- 1300 South Second Street; Minneapolis, MN 55454, USA;
-
- This file is part of geomview/OOGL. geomview/OOGL is free software;
- you can redistribute it and/or modify it only under the terms given in
- the file COPYING, which you should have received along with this file.
- This and other related software may be obtained via anonymous ftp from
- geom.umn.edu; email: software@geom.umn.edu. */
-
- /* Authors: Charlie Gunn, Stuart Levy, Tamara Munzner, Mark Phillips */
-
- /*
- * light -
- * Support for describing light sources and lists of
- * light sources.
- *
- * Pat Hanrahan 1989
- */
- #include "appearance.h"
- #include "ooglutil.h"
-
- static void norm();
-
- /*
- * Default light is full white and position along the z-axis.
- */
-
- static Color black = { 0.0, 0.0, 0.0 };
-
- LtLight *
- _LtSet(LtLight *light, int a1, va_list *alist)
- {
-
- int attr;
- Color *co;
- Point *pt;
- char **ablock = NULL;
-
- #define NEXT(type) OOGL_VA_ARG(type,alist,ablock)
-
- if (light == NULL) {
- /*
- * New LtLight created here.
- */
- light = OOGLNewE(LtLight, "LtCreate LtLight");
- LtDefault(light);
- }
-
- for(attr = a1; attr != LT_END; attr = NEXT(int)) {
- switch (attr) { /* parse argument list */
- case LT_ABLOCK:
- ablock = NEXT(char **);
- break;
- case LT_AMBIENT:
- co = NEXT(Color *);
- CoCopy(co, &light->ambient);
- light->changed = 1;
- break;
- case LT_COLOR:
- co = NEXT(Color *);
- CoCopy(co, &light->color);
- light->changed = 1;
- break;
- case LT_POSITION:
- pt = NEXT(Point *);
- PtCopy(pt, &light->position);
- light->changed = 1;
- break;
- case LT_INTENSITY:
- light->intensity = NEXT(double);
- light->changed = 1;
- break;
- case LT_LOCATION:
- light->location = NEXT(int);
- light->changed = 1;
- break;
- default:
- OOGLError (0, "_LtSet: undefined option: %d\n", attr);
- return NULL;
- break;
- }
- }
- return light;
- #undef NEXT
- }
-
- LtLight *
- LtCreate(int a1, ... )
- {
- va_list alist;
- LtLight *light;
-
- va_start(alist,a1);
- light = _LtSet(NULL, a1, &alist);
- va_end(alist);
- return light;
- }
-
- LtLight *
- LtSet(LtLight *light, int attr, ...)
- {
- va_list alist;
-
- va_start(alist,attr);
- light = _LtSet(light,attr,&alist);
- va_end(alist);
- return light;
- }
-
- int
- LtGet(LtLight *light, int attr, void * value)
- {
- if (!light) return NULL;
-
- switch (attr) {
- case LT_AMBIENT:
- *(Color *) value = light->ambient;
- break;
- case LT_COLOR:
- *(Color *) value = light->color;
- break;
- case LT_POSITION:
- *(Point *) value = light->position;
- break;
- case LT_INTENSITY:
- *(double *) value = light->intensity;
- break;
- case LT_LOCATION:
- *(int *)value = light->location;
- break;
- default:
- OOGLError (0, "LtGet: undefined option: %d\n", attr);
- return -1;
- break;
- }
- return 1;
- }
-
- void
- LtDelete(LtLight *l)
- {
- free(l);
- }
-
- LtLight *
- LtCopy( register LtLight *l1, register LtLight *l2 )
- {
- if(l2 == NULL)
- l2 = OOGLNewE(LtLight, "LtCopy LtLight");
- *l2 = *l1; /* Don't reset the 'changed' & 'Private' fields */
- l2->next = NULL;
- /* Reset private and changed flags */
- l2->Private = 0;
- l2->changed = 1;
- return l2;
- }
-
-
- LtLight *
- LtMerge( register LtLight *l1, register LtLight *l2 )
- {
- if(l2 == NULL)
- l2 = OOGLNewE(LtLight, "LtMerge LtLight");
- *l2 = *l1;
- /* Don't reset the 'changed' & 'Private' fields */
- l2->next = NULL;
- return l2;
- }
-
- LtLight *
- LtDefault( LtLight *light )
- {
- static HPoint3 defposition = { 0.0, 0.0, 1.0, 0.0 };
- static Color deflight = { 1.0, 1.0, 1.0 };
-
- light->next = NULL;
- light->intensity = 1.0;
- light->ambient = black;
- light->color = deflight;
- light->position = defposition;
- light->location = LTF_GLOBAL;
- light->Private = 0;
- light->changed = 1;
- }
-
- /*
- * Set the intensity, color and position of a light.
- */
- void
- LtProperties( LtLight *light, float intensity, Color *color, Point *position )
- {
- light->intensity = intensity;
- light->color = *color;
- light->position = *position;
- light->location = LTF_GLOBAL;
-
- light->changed = 1;
- }
-
- /*
- * Load a lighting description from a file.
- */
- LtLight *
- LtLoad(LtLight *li, char *name)
- {
- FILE *f;
-
- if(name == NULL || (f = fopen(name, "r")) == NULL) {
- OOGLError(1, "Can't find light file %s: %s", name, sperror());
- return NULL;
- }
- li = LtFLoad(li, f, name);
- fclose(f);
- return li;
- }
-
- /*
- * Load Light from file.
- * Syntax:
- * < "filename_containing_material" [or]
- * { keyword value keyword value ... }
- *
- */
-
- LtLight *
- LtFLoad(lite, f, fname)
- LtLight *lite;
- FILE *f;
- char *fname; /* Used for error msgs, may be NULL */
- {
- char *w;
- register int i,j;
- float v[4];
- int brack = 0;
- static char *lkeys[] = {
- "ambient", "color", "position", "location", "global", "camera", "local"
- };
- static short largs[] = { 3, 3, 4, 0, ~LTF_GLOBAL, ~LTF_CAMERA, ~LTF_LOCAL };
- int got;
- LtLight l;
-
- LtDefault(&l);
-
- for(;;) {
- switch(fnextc(f, 0)) {
- case '<':
- fgetc(f);
- if(LtLoad(&l, fdelimtok("(){}", f, 0)) == NULL) return NULL;
- if(!brack) goto done;
- break;
- case '{': brack++; fgetc(f); break;
- case '}': if(brack) { fgetc(f); } goto done;
- default:
- w = ftoken(f, 0);
- if(w == NULL)
- goto done;
-
- for(i = sizeof(lkeys)/sizeof(lkeys[0]); --i >= 0; )
- if(!strcmp(w, lkeys[i]))
- break;
-
- if( i < 0) {
- OOGLSyntax(f, "Reading light from %s: unknown keyword %s",fname,w);
- return NULL;
- } else if( largs[i] > 0 && (got=fgetnf(f, largs[i], v, 0)) != largs[i] ) {
- OOGLSyntax(f, "Reading light from %s: \"%s\" expects %d values, got %d",
- fname, w, largs[i], got);
- return NULL;
- }
- switch(i) {
- case 0: l.ambient = *(Color *)v; break;
- case 1: l.color = *(Color *)v;
- norm( &l.color, &l.intensity ); break;
- case 2: l.position = *(Point *)v; break;
- case 3: break;
- default: l.location = ~largs[i]; break;
- }
- }
- }
- done:
- lite = LtCopy(&l, lite);
- return lite;
- }
-
- void
- LtFSave( LtLight *l, FILE *f )
- {
- fprintf(f,"\t\tambient %f %f %f\n",
- l->ambient.r,
- l->ambient.g,
- l->ambient.b);
- fprintf(f,"\t\tcolor %f %f %f\n",
- l->intensity*l->color.r,
- l->intensity*l->color.g,
- l->intensity*l->color.b);
- fprintf(f,"\t\tposition %f %f %f %f\n",
- l->position.x,
- l->position.y,
- l->position.z,
- l->position.w);
- if(l->location != LTF_GLOBAL)
- fprintf(f, "\t\tlocation %s\n",
- l->location == LTF_CAMERA ? "camera" : "local");
- /*
- fprintf(f,"intensity %f\n", la->intensity);
- */
- }
-
- /*
- * Remove a light from a light source list. However,
- * this routine doesn't actually delete it.
- */
- void
- LtRemove( LmLighting *lighting, LtLight *light )
- {
- LtLight *l;
-
- if( lighting->lights == light ) {
- lighting->lights = lighting->lights->next;
- }
- else {
- for( l=lighting->lights; l->next; l=l->next )
- if( l->next == light ) {
- l->next = l->next->next;
- break;
- }
- }
- }
-
- void
- LtAppend( LmLighting *lighting, LtLight *light )
- {
- LtLight *l;
-
- if( lighting->lights ) {
- for( l=lighting->lights; l->next; l=l->next )
- ;
- l->next = light;
- }
- else
- lighting->lights = light;
- }
-
- /*
- * Create a list of lights and a lighting model.
- */
- LmLighting *
- _LmSet(LmLighting *lgt, int a1, register va_list *alist)
- {
- int attr;
- Color *co;
- LtLight *la;
- int v, mask;
- char **ablock = NULL;
-
- #define NEXT(type) OOGL_VA_ARG(type,alist,ablock)
-
- if (!alist) return lgt;
- if (lgt == NULL) {
- /*
- * New Lighting created here.
- */
- lgt = OOGLNewE(LmLighting, "LmCreate Lighting");
- LmDefault(lgt);
- }
-
- for(attr = a1; attr != LM_END; attr = NEXT(int)) {
- switch (attr) { /* parse argument list */
- case LM_ABLOCK:
- ablock = NEXT(char **);
- break;
- case LM_LtSet:
- la = ablock ? LtSet(NULL, LT_ABLOCK, ablock)
- : _LtSet(NULL, va_arg(*alist, int), alist);
- la->next = lgt->lights;
- lgt->lights = la;
- break;
- case LM_LIGHT:
- lgt->lights = NEXT(LtLight *);
- break;
- case LM_REPLACELIGHTS:
- if (NEXT(int))
- lgt->valid |= LMF_REPLACELIGHTS;
- else
- lgt->valid &= ~LMF_REPLACELIGHTS;
- break;
- case LM_AMBIENT:
- co = NEXT(Color *);
- CoCopy(co, &lgt->ambient);
- lgt->valid |= LMF_AMBIENT;
- break;
- case LM_LOCALVIEWER:
- lgt->localviewer = NEXT(double);
- lgt->valid |= LMF_LOCALVIEWER;
- break;
- case LM_ATTENC:
- lgt->attenconst = NEXT(double);
- lgt->valid |= LMF_ATTENC;
- break;
- case LM_ATTENM:
- lgt->attenmult = NEXT(double);
- lgt->valid |= LMF_ATTENM;
- break;
- case LM_OVERRIDE:
- lgt->override |= NEXT(int);
- break;
- case LM_NOOVERRIDE:
- lgt->override &= ~NEXT(int);
- break;
- case LM_INVALID:
- lgt->valid &= ~NEXT(int);
- break;
- default:
- OOGLError (0, "_LmSet: undefined option: %d\n", attr);
- return NULL;
- break;
- }
- }
-
- return lgt;
-
- #undef NEXT
- }
-
- LmLighting *
- LmCreate(int attr, ... )
- {
- va_list alist;
- LmLighting *lgt;
-
- va_start(alist,attr);
- lgt = _LmSet(NULL, attr, &alist);
- va_end(alist);
- return lgt;
- }
-
-
- LmLighting *
- LmSet(LmLighting *lgt, int a1, ... )
- {
- va_list alist;
- va_start(alist,a1);
- lgt = _LmSet(lgt, a1, &alist);
- va_end(alist);
- return lgt;
- }
-
-
- int
- LmGet(LmLighting *lgt, int attr, void *value)
- {
- if (!lgt) return NULL;
-
- switch (attr) {
- case LM_LIGHT:
- *(LtLight **) value = lgt->lights;
- break;
- case LM_REPLACELIGHTS:
- *(int*)value = lgt->valid & LMF_REPLACELIGHTS;
- break;
- case LM_AMBIENT:
- *(Color *) value = lgt->ambient;
- break;
- case LM_LOCALVIEWER:
- *(double *) value = lgt->localviewer;
- break;
- case LM_ATTENC:
- *(double *) value = lgt->attenconst;
- break;
- case LM_ATTENM:
- *(double *) value = lgt->attenmult;
- break;
- case LM_ATTEN2:
- *(double *) value = lgt->attenmult2;
- break;
- case LM_OVERRIDE:
- case LM_NOOVERRIDE:
- *(int *) value = lgt->override;
- break;
- case LM_VALID:
- case LM_INVALID:
- *(int *) value = lgt->valid;
- break;
- default:
- OOGLError (0, "LmGet: undefined option: %d\n", attr);
- return -1;
- break;
- }
- return 1;
- }
-
- LmLighting *
- LmMerge(LmLighting *src, LmLighting *dst, int mergeflags)
- {
- unsigned int mask;
-
- if(dst == NULL)
- return LmCopy(src, NULL);
-
- mask = src ?
- (mergeflags & APF_OVEROVERRIDE) ?
- src->valid : src->valid & ~(dst->override &~ src->override)
- : 0;
-
- if(src == NULL || (mask == 0 && src->lights == NULL)) {
- RefIncr((Ref *)dst);
- return dst;
- }
-
- if(mask && !(mergeflags & APF_INPLACE))
- dst = LmCopy(dst, NULL);
- dst->changed |= src->changed;
- dst->valid = (src->valid & mask) | (dst->valid & ~mask);
- dst->override = (src->override & mask) | (dst->override & ~mask);
- if(mask & LMF_LOCALVIEWER) dst->localviewer = src->localviewer;
- if(mask & LMF_AMBIENT) dst->ambient = src->ambient;
- if(mask & LMF_ATTENC) dst->attenconst = src->attenconst;
- if(mask & LMF_ATTENM) dst->attenmult = src->attenmult;
- if(mask & LMF_ATTEN2) dst->attenmult2 = src->attenmult2;
- if(src->lights != dst->lights) {
- if((mask & LMF_REPLACELIGHTS) && dst->lights) {
- /* LMF_REPLACELIGHTS: replace lights rather than merging with them */
- LtDeletelist(dst->lights);
- dst->lights = NULL;
- }
- if(src->lights) {
- LtLight *sl;
- register LtLight *hl;
-
- hl = sl = LtCopylist(src->lights, 1); /* merge not copy */
- while(sl->next)
- sl = sl->next;
- sl->next = dst->lights;
- dst->lights = hl;
- }
- }
-
- RefIncr((Ref *)dst);
- return dst;
- }
-
- LmLighting *
- LmCopy(register LmLighting *from, register LmLighting *to)
- {
- if (!from) return NULL;
- if(to == NULL) {
- to = OOGLNewE(LmLighting, "LmCopy LmLighting");
- *to = *from;
- RefInit((Ref *)to, LIGHTINGMAGIC);
- to->Private = 0;
- to->lights = LtCopylist(from->lights, 0);
- } else if (from != to) {
- Ref r;
- r = *(Ref *)to;
- LtDeletelist(to->lights);
- *to = *from;
- to->lights = LtCopylist(from->lights, 0);
- *(Ref *)to = r;
- }
-
- return to;
- }
-
- void
- LmDefault( LmLighting *l )
- {
- RefInit((Ref *)l, LIGHTINGMAGIC);
- l->valid = l->override = 0;
- l->ambient = black;
- l->localviewer = 1;
- l->attenconst = 0.0;
- l->attenmult = 0.0;
- l->lights = NULL;
- l->changed = 1;
- l->Private = 0;
- }
-
- /*
- * Delete a list of lights and all the lights in the list.
- */
- void
- LmDelete(LmLighting *lm)
- {
- register LtLight *l, *nl;
-
- if(lm == NULL || RefDecr((Ref *)lm) > 0)
- return;
- LtDeletelist(lm->lights);
- free(lm);
- }
-
- void
- LtDeletelist(register LtLight *l)
- {
- register LtLight *nl;
-
- while(l) {
- nl = l->next;
- LtDelete(l);
- l = nl;
- }
- }
-
- LtLight *
- LtCopylist(LtLight *l, int mergeflag)
- {
- register LtLight **tailp;
- LtLight *new;
-
- for(tailp = &new; l != NULL; l = l->next) {
- if (mergeflag & APF_INPLACE)
- *tailp = LtMerge(l, NULL);
- else
- *tailp = LtCopy(l, NULL);
- tailp = &(*tailp)->next;
- }
- *tailp = NULL;
- return new;
- }
-
- #define max(a,b) (a)>(b)?(a):(b)
-
- static void
- norm( color, coeff )
- Color *color;
- float *coeff;
- {
- *coeff = max(color->r, color->g);
- *coeff = max(color->b, *coeff);
-
- if( *coeff != 0.0 ) {
- color->r /= *coeff;
- color->g /= *coeff;
- color->b /= *coeff;
- }
- }
-
- /*
- * Load a lighting description from a file.
- */
- LmLighting *
- LmLoad(LmLighting *li, char *name)
- {
- FILE *f;
- LmLighting *ls;
-
- f = fopen(name,"r");
- if(!f) {
- perror(name);
- return 0;
- }
- ls = LmFLoad(li, f, name);
- fclose(f);
- return ls;
- }
-
-
- /*
- * Load Lighting from file.
- * Syntax:
- * < "filename_containing_material" [or]
- * { keyword value keyword value ... }
- *
- * Each keyword may be prefixed by "*", indicating that its value should
- * override corresponding settings in child objects. [By default,
- * children's appearance values supercede those of their parents.]
- *
- */
-
- LmLighting *
- LmFLoad(lgt, f, fname)
- LmLighting *lgt;
- FILE *f;
- char *fname; /* Used for error msgs, may be NULL */
- {
- char *w;
- register int i;
- float v[3];
- int brack = 0;
- int over, not;
- static char *lkeys[] = {
- "ambient", "localviewer", "attenconst", "attenmult", "attenmult2", "light",
- "replacelights"
- };
- static char largs[] = { 3, 1, 1, 1, 1, 0, 0};
- static unsigned short lbits[] = {
- LMF_AMBIENT, LMF_LOCALVIEWER, LMF_ATTENC, LMF_ATTENM, LMF_ATTEN2, 0, LMF_REPLACELIGHTS
- };
- int got;
- LmLighting l;
- LtLight lite;
- LtLight *last, *new;
-
- LmDefault(&l);
-
- over = not = 0;
- new = l.lights;
- last = NULL;
-
- for(;;) {
- switch(fnextc(f, 0)) {
- case '<':
- fgetc(f);
- if(LmLoad(&l, ftoken(f, 0)) == NULL) return NULL;
- if(!brack) goto done;
- break;
- case '{': brack++; fgetc(f); break;
- case '}': if(brack) { fgetc(f); } goto done;
- case '*': over = 1; fgetc(f); break; /* 'override' prefix */
- case '!': not = 1; fgetc(f); break;
- default:
- w = ftoken(f, 0);
- if(w == NULL)
- return LmCopy(&l, lgt);
- /* break; */ /* done */
-
- for(i = sizeof(lkeys)/sizeof(lkeys[0]); --i >= 0; )
- if(!strcmp(w, lkeys[i]))
- break;
-
- if( i < 0) {
- OOGLError(1, "LmFLoad: %s: unknown lighting keyword %s",fname,w);
- return NULL;
- } else if( !not && (got=fgetnf(f, largs[i], v, 0)) != largs[i] ) {
- OOGLError(1, "LmFLoad: %s: \"%s\" expects %d values, got %d",
- fname, w, largs[i], got);
- return NULL;
- }
-
- if(not) {
- if(!over) l.valid &= ~lbits[i];
- l.override &= ~lbits[i];
- } else {
- l.valid |= lbits[i];
- if(over) l.override |= lbits[i];
- switch(i) {
- case 0: l.ambient = *(Color *)v; break;
- case 1: l.localviewer = v[0]; break;
- case 2: l.attenconst = v[0]; break;
- case 3: l.attenmult = v[0]; break;
- case 4: l.attenmult2 = v[0]; break;
- case 5:
- LtFLoad( &lite, f, fname );
- new = LtCreate(LT_END);
- if (!last)
- l.lights = new;
- else
- last->next = new;
- LtCopy( &lite, new);
- last = new;
- new = new->next;
- break;
- }
- }
- over = not = 0;
- }
- }
- done:
- return LmCopy(&l, lgt);
- }
-
-
- /*
- * Save a light description in a file.
- */
-
- LmFSave(LmLighting *li, FILE *f, char *fname)
- {
- register LtLight *la;
-
- fprintf(f,"\tambient %g %g %g\n",
- li->ambient.r,
- li->ambient.g,
- li->ambient.b);
- fprintf(f,"\tlocalviewer %d\n",li->localviewer);
- fprintf(f,"\tattenconst %g\n",li->attenconst);
- fprintf(f,"\tattenmult %g\n",li->attenmult);
- if(li->valid & LMF_ATTEN2) fprintf(f,"\tattenmult2 %g\n",li->attenmult2);
- if (li->valid & LMF_REPLACELIGHTS) fprintf(f,"\treplacelights\n");
- la = li->lights;
- while(la) {
- fprintf(f, "\tlight {\n");
- LtFSave( la, f );
- fprintf(f, "\t}\n");
- la = la->next;
- }
- }
-